/*
 *  SPUKMK3me - Rendering library for 2D game.
 *  Copyright 2012  H2KPigman
 *
 *   SPUKMK2me is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *   SPUKMK2me is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *  along with SPUKMK2me.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.spukmk3me.util;

import java.io.InputStream;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

import com.spukmk3me.math.FPMath;

/**
 *  Provide some utility functions that are frequently used by game developer.
 *  \details Included stuff:\n
 *  - Random numbers (wrapped from standard library).\n
 *  - ReadWord/ReadLine functions used in text reading.\n
 *  - Some function for handling 16-16 fixed-point real number.
 */
public final class Util
{
    /**
     *  Initialize the random system.
     *  \details Mimics CLDC 1.1 java.util.Random.setSeed( long ).
     */
    public static void initialiseRandomSeed( long seed )
    {
        RANDSEED = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
    }

    /**
     *  \details This is a linear congruential pseudorandom number generator,
     * as defined by D. H. Lehmer and described by Donald E. Knuth in
     * The Art of Computer Programming, Volume 2: Seminumerical Algorithms,
     * section 3.2.1.
     *  @param bits Random bits
     *  @return The next pseudorandom value from this random number generator's
     * sequence
     */
    private static int getRandNext( int bits )
    {
        RANDSEED = (RANDSEED * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
        return (int)(RANDSEED >>> (48 - bits));
    }

    /**
     *  Generate the next random number.
     *  \details Mimics CLDC 1.1 java.util.Random.nextInt( int ).
     *  @param bound The bound of generated integer.
     *  @return A pseudo-randomized number which is smaller than bound and
     * greater or equal 0.
     */
    public static int rand( int bound )
    {        
        if ( bound <= 0 )
            return 0;

        if ( (bound & -bound) == bound ) // i.e., n is a power of 2
            return (int)((bound * (long)getRandNext( 31 )) >> 31);

        int bits, val;
        
        do
        {
            bits    = getRandNext( 31 );
            val     = bits % bound;
        } while (bits - val + (bound - 1) < 0);
        
        return val;
    }

    /**
     *   Read a ANSI-encoded line of text.
     *   \details Can only deal with end-of-line character CR/LF and LF/CR.
     *  Users must provide their own buffer because allocation/deallocation
     *  on Java is unreliable.\n
     *   \details User must remove any extra bytes, such as BOM in the input
     * stream before passing the stream to this function.
     *   @note Current implement requires buffer size is at least
     *  max_bytes_per_line + 2.
     *   @param is Input stream.
     *   @param buffer User-provided buffer, for avoiding mass object
     * instantiation.
     */
    public static String readUTF8Line( InputStream is, byte[] buffer )
        throws IOException
    {
        int     i = 2;
        byte    c = 0;
        DataInputStream dis = new DataInputStream( is );

        while ( (dis.available() > 0) && (i < buffer.length) )
        {
            c = dis.readByte();

            if ( (c == 13) || (c == 10) )
                break;

            buffer[ i++ ] = c;
        }

        // Read LF (or CR)
        if ( dis.available() != 0 )
            dis.readByte();

        buffer[ 0 ] = (byte)(i - 2 >> 8);
        buffer[ 1 ] = (byte)((i - 2) & 0x000000FF);
        return new DataInputStream( new ByteArrayInputStream( buffer, 0, i ) ).
            readUTF();
    }

    /**
     *  Read the incoming word in the input stream.
     *  \details End-of-line characters, EOF, spaces and TABs are removed
     *  from the word.
     *   \details User must remove any extra bytes, such as BOM in the input
     * stream before passing the stream to this function.
     *   @note Current implement requires buffer size is at least
     *  max_bytes_per_line + 2.
     *   @param is Input stream.
     *   @param buffer User-provided buffer, for avoiding mass object
     * instantiation.
     */
    public static String readUTF8Word( InputStream is, byte[] buffer )
        throws IOException
    {
        if ( is.available() == 0 )
            return "";

        int     i = 2;
        byte    c = 0;
        DataInputStream dis = new DataInputStream( is );

        do
        {
            c = dis.readByte();
        } while ( ((c == 32) || (c == 8) || (c == 10) || (c == 13)) &&
                (dis.available() > 0) );

        buffer[ i++ ] = c;

        while ( (dis.available() > 0) && (i < buffer.length) )
        {
            c = dis.readByte();

            if ( (c == 13) || (c == 10) || (c == 32) || (c == 8) )
                break;

            buffer[ i++ ] = c;
        }

        buffer[ 0 ] = (byte)(i - 2 >> 8);
        buffer[ 1 ] = (byte)((i - 2) & 0x000000FF);
        return new DataInputStream( new ByteArrayInputStream( buffer, 0, i ) ).
            readUTF();
    }

    /**
     *  Test if two rectangles intersect.
     *  @param x1 X coordinate of top-left point of the first rectangle.
     *  @param y1 Y coordinate of top-left point of the first rectangle.
     *  @param w1 Width of the first rectangle.
     *  @param h1 Height of the first rectangle.
     *  @param x2 X coordinate of top-left point of the second rectangle.
     *  @param y2 X coordinate of top-left point of the second rectangle.
     *  @param w2 Width of the second rectangle.
     *  @param h2 Height of the second rectangle.
     *  @return true if two rectangle intersect
     */
    public static boolean rectIntersect(
        int x1, int y1, int w1, int h1,
        int x2, int y2, int w2, int h2 )
    {
        return  (x1 < x2 + w2) && (y1 < y2 + h2) &&
                (x2 < x1 + w1) && (y2 < y1 + h1);
    }

    public static boolean isLegalName( String name )
    {
        if ( name == null )
            return false;
        
        if ( name.length() == 0 )
            return false;
        
        int length = name.length();
        char c;
        
        for ( int i = 0; i != length; ++i )
        {
            c = name.charAt( i );

            if ( !(( c >= 'a') && ( c <= 'z')) )
            {
                if ( !(( c >= '0') && ( c <= '9')) )
                {
                    if ( c != '_' )
                        return false;
                }
            }
        }
        
        return true;
    }
    
    public static int addColor( int color1, int color2 )
    {
        int color, c1, c2;
        
        c1      = (color1 >> 24) & 0x00FF;
        c2      = (color2 >> 24) & 0x00FF;
        color   = ((c1 * c2 / 255 + c1 + c2) & 0x00FF) << 24;
        
        c1      = (color1 >> 16) & 0x00FF;
        c2      = (color2 >> 16) & 0x00FF;
        color  |= ((c1 * c2 / 255 + c1 + c2) & 0x00FF) << 16;
        
        c1      = (color1 >> 8) & 0x00FF;
        c2      = (color2 >> 8) & 0x00FF;
        color  |= ((c1 * c2 / 255 + c1 + c2) & 0x00FF) << 8;
        
        c1      = color1 & 0x00FF;
        c2      = color2 & 0x00FF;
        color  |= (c1 * c2 / 255 + c1 + c2) & 0x00FF;
        
        return color;
    }
    
    public static int mulColor( int color1, int color2 )
    {
        int color, temp;
        
        temp    = ((color1 >> 24) & 0x00FF) * ((color2 >> 24) & 0x00FF) / 255;
        color   = temp << 24;
        
        temp    = ((color1 >> 16) & 0x00FF) * ((color2 >> 16) & 0x00FF) / 255;
        color  |= temp << 16;
        
        temp    = ((color1 >> 8) & 0x00FF) * ((color2 >> 8) & 0x00FF) / 255;
        color  |= temp << 8;
        
        temp    = (color1 & 0x00FF) * (color2 & 0x00FF) / 255;
        color  |= temp;
        
        return color;
    }
    
    public static int[] convertMilisecToFP( int[] milisecs )
    {
        for ( int i = 0; i != milisecs.length; ++i )
            milisecs[ i ] = FPMath.fpDiv( milisecs[ i ] << 16, 0x03E80000 );
        
        return milisecs;
    }
    
    public static int convertMilisecToFP( int milisec )
    {
        return FPMath.fpDiv( milisec << 16, 0x03E80000 );
    }
    
    public static final int PI = 0;

    private static long RANDSEED;
}
